#pragma once
#include <iostream>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Command.hpp"

#include "Log.hpp"
#include "InetAddr.hpp"

using namespace log_ns;

//设置函数对象
using command_service_t = std::function<void(int sockfd, InetAddr addr)>;

enum
{
  SOCKET_ERROR = 1,
  BIND_ERROR,
  LISTEN_ERROR
};

const static int gport = 8888;
const static int glistensockfd = -1;
const static int gblcklog = 8;
class TcpServer
{

public:
  TcpServer(command_service_t service, uint16_t port = gport) 
  : _port(port)
  , _listensockfd(glistensockfd)
  , _isrunning(false)
  , _service(service)
  {
  }

  void InitServer()
  {
    // 1.创建socket
    _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if (_listensockfd < 0)
    {
      LOG(FATAL, "socker create error\n");
      exit(SOCKET_ERROR);
    }
    LOG(INFO, "socket create success, sockfd: %d\n", _listensockfd);


    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = INADDR_ANY;
    // 2.绑定sockfd 和 socket addr
    if (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
      LOG(FATAL, "bind error \n");
      exit(BIND_ERROR);
    }
    LOG(INFO, "bind success \n");

    // 3.因为tcp是面向连接的，就需要tcp未来不断的能够做到获取链接

    // 让套接字设置为listen状态
    if (::listen(_listensockfd, gblcklog))
    {
      LOG(FATAL, "listen error \n");
      exit(LISTEN_ERROR);
    }
    LOG(INFO, "listen success \n");
  }

  class ThreadData
  {
  public:
    int _sockfd;
    TcpServer *_self;
    InetAddr _addr;//给InetAddr再添加一个无参构造
  public:
    ThreadData(int sockfd, TcpServer *self, const InetAddr &addr) : _sockfd(sockfd), _self(self), _addr(addr) 
    {
    }
  };

  void Loop()
  {
    // signal(SIGCHLD, SIG_IGN);
    _isrunning = true;
    while (_isrunning)
    {
      struct sockaddr_in client;
      socklen_t len = sizeof(client);

      // 4.获取新链接
      // 从监听套接字获取新的套接字、获取客户端信息
      int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);
      // 获取连接失败、继续获取
      if (sockfd < 0)
      {
        LOG(WARNING, "accept error\n");
        continue;
      }
      InetAddr addr(client);
      // 获客成功，提供服务
      LOG(INFO, "get a new link, client info : %s, sockfd is: %d\n", addr.AddrStr().c_str(), sockfd);
      
      // version2-----多线程版本 --- 不能关闭fd,也不需要
      pthread_t tid;
      ThreadData* td = new ThreadData(sockfd, this, addr);
      pthread_create(&tid, nullptr, Execute, td);


    }
    _isrunning = false;
  }

  static void *Execute(void *args)
  {
    pthread_detach(pthread_self());

    ThreadData* td = static_cast<ThreadData *>(args);
    //直接回调我们定义的函数对象
    td->_self->_service(td->_sockfd, td->_addr);
    //处理完了，直接关闭
    ::close(td->_sockfd);

    delete td;
    return nullptr;
  }

  // void Service(int sockfd, InetAddr addr)
  // {
  
  //   // ::close(sockfd);
  // }

  ~TcpServer()
  {
  }

private:
  uint16_t _port;
  int _listensockfd; // todo
  bool _isrunning;

  command_service_t _service;
};
